//
//  PSGame.m
//  PrefCore
//
//  Created by Sergey on 1/16/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import "PSGame.h"

@interface PSPlayerData () {
    PSHandData *_hands[PS_GAME_PLAYERS];
    PSGameBidData *_bids[PS_GAME_PLAYERS];
    PSScore *_scores[PS_GAME_PLAYERS];
    int _tricks[PS_GAME_PLAYERS];
}

@end

@interface PSTrickData () {
    NSMutableArray *_listOfTricks;
}

@end

@implementation PSGameBidData

-(instancetype)init {
    if (self = [super init]) {
        _current = nil;
        _max = nil;
        _count = 0;
        _isVisible = NO;
    }
    return self;
}

-(void)reset {
    _current = nil;
    _max = nil;
    _count = 0;
    _isVisible = NO;
}

@end

@implementation PSPlayerData

-(instancetype)init {
    if (self = [super init]) {
        _dealer = arc4random_uniform(PS_GAME_PLAYERS);
        _starting = PS_NO_PLAYER_ID;
        _dealer = PS_NO_PLAYER_ID;
        _opponent = PS_NO_PLAYER_ID;
        _passer = PS_NO_PLAYER_ID;
        _half = PS_NO_PLAYER_ID;
        _current = PS_NO_PLAYER_ID;
        for (int i = 0; i != PS_GAME_PLAYERS ; ++i) {
            _hands[i] = [[PSHandData alloc] init];
            _bids[i] = [[PSGameBidData alloc] init];
            _scores[i] = [[PSScore alloc] init];
            _tricks[i] = 0;
        }
    }
    return self;
}

-(void)reset:(PSPlayerId)identifier open:(BOOL)open {
    // clear cards
    [_hands[identifier] reset:open];
    // clear bids
    [_bids[identifier] reset];
    // clear tricks count
    _tricks[identifier] = 0;
}

-(PSScore* __strong*)scores {
    return _scores;
}

-(PSGameBidData* __strong*) bids {
    return _bids;
}

-(PSHandData* __strong *)hands {
    return _hands;
}

-(int*)tricks {
    return _tricks;
}

@end

@implementation PSHandData

-(instancetype)init {
    if (self = [super init]) {
        _initial = 0;
        _current = 0;
        _open = NO;
    }
    return self;
}

-(void)reset:(BOOL)open {
    _current = 0;
    _open = open;
}

@end

@implementation PSTrickData

-(instancetype)init {
    if (self = [super init]) {
        _last = 0;
        _current = 0;
        _listOfTricks = [NSMutableArray array];
    }
    return self;
}

-(void)reset {
    _last = 0;
    _current = 0;
    _listOfTricks = [NSMutableArray array];
}

-(NSArray*)listOfTricks {
    return [_listOfTricks copy];
}

-(PSHandHash)trashCurrentWithOldTrash:(PSHandHash)trash {
    // copy current trick data to the previous one
    _last = _current;
    // add trick to the list
    [_listOfTricks addObject:@(_last)];
    // trash current trick cards
    trash = PSHandAddTrick(trash, _current);
    // reset current trick
    _current = 0;
    return trash;
}

-(PSTrickCount)tricksOfPlayer:(PSPlayerId)playerId withTrump:(PSCardSuit)trump {
    PSTrickCount res = 0;
    for (NSNumber *hash in _listOfTricks) {
        if (PSTrickWinner(hash.unsignedIntValue, trump) == playerId) {
            ++res;
        }
    }
    return res;
}

@end

extern inline PSPlayerId PSGameNextPlayerId(PSPlayerId playerId);

extern inline PSPlayerId PSGamePreviousPlayerId(PSPlayerId playerId);

@interface PSGame () {
    PSScore *_scores[PS_GAME_PLAYERS];
}

-(PSPlayer*)playerForId:(PSPlayerId)identifier;

@end

@implementation PSGame

-(instancetype)init {
    if (self = [super init]) {
        _options = [[PSGameInputOptions alloc] init];
        _statistics = [[PSStatistics alloc] init];
        _players = @[
           [[PSPlayer alloc] initWithId:0] // FIXME Human
        ,  [[PSPlayer alloc] initWithId:1] // FIXME AI #1
        ,  [[PSPlayer alloc] initWithId:2] // FIXME AI #2
        ];
        for (int i = 0 ; i != PS_GAME_PLAYERS ; i++) {
            _scores[i] = [[PSScore alloc] init];
        }
        _player = [[PSPlayerData alloc] init];
        _trick = [[PSTrickData alloc] init];
    }
    return self;
}

-(PSPlayer*)playerForId:(PSPlayerId)playerId {
    NSAssert(playerId < PS_GAME_PLAYERS, @"Player identifier is out of range.");
    return _players[playerId];
}

-(PSScore*)scoreForPlayerId:(PSPlayerId)playerId {
    NSAssert(playerId < PS_GAME_PLAYERS, @"Player identifier is out of range.");
    return _scores[playerId];
}

-(PSPlayer*)nextPlayer:(PSPlayer*)player {
    return _players[PSGameNextPlayerId(player.identifier)];
}

-(PSCardSuit)trump {
    return _player.declarer == PS_NO_PLAYER_ID ? kSuitNone : [self declarerBid].current.trump;
}

-(PSPlayer*)previousPlayer:(PSPlayer*)player {
    return _players[(player.identifier + PS_GAME_PLAYERS - 1) % PS_GAME_PLAYERS];
}

-(PSTrickCount)estimateHumanTrickCountWithSettings:(PSGameSettings *)settings {
    // FIXME
    return 4;
}

-(PSBid*)estimateHumanContract {
    // FIXME
    return [PSBid bidNone];
}

-(void)resetConsecutivePassingSets {
    _consecutivePassingSets = 0;
}

-(void)incrementConsecutivePassingSets {
    _consecutivePassingSets++;
}

-(PSTrickCount)tricksOfPlayer:(PSPlayerId)identifier {
    return _player.tricks[identifier];
}

-(PSTrickCount)declarerTricks {
    return [self tricksOfPlayer:_player.declarer];
}

-(PSTrickCount)opponentTricks {
    return [self tricksOfPlayer:_player.opponent];
}

-(PSTrickCount)passerTricks {
    return [self tricksOfPlayer:_player.passer];
}

-(void) setTricks:(PSTrickCount)tricks ofPlayer:(PSPlayerId)identifier {
    _player.tricks[identifier] = tricks;
}

-(void) addTricks:(PSTrickCount)tricks ofPlayer:(PSPlayerId)identifier {
    _player.tricks[identifier] += tricks;
}

-(PSTrickCount)contract {
    return self.declarerBid.current.tricks;
}

-(PSPlayer*)dealer {
    return [self playerForId:_player.dealer];
}

-(PSPlayer*)declarer {
    return [self playerForId:_player.declarer];
}

-(PSPlayer*)opponent {
    return [self playerForId:_player.opponent];
}

-(PSPlayer*)passer {
    return [self playerForId:_player.passer];
}

-(PSPlayer*)half {
    return [self playerForId:_player.half];
}

-(PSPlayer*)starting {
    return [self playerForId:_player.starting];
}

-(PSPlayer*)current {
    return [self playerForId:_player.current];
}

-(PSHandData*) declarerHand {
    return _player.hands[_player.declarer];
}

-(PSHandData*) humanHand {
    return _player.hands[0];
}

-(PSHandData*) currentPlayerHand {
    return _player.hands[_player.current];
}

-(PSHandData*) opponentHand {
    return _player.hands[_player.opponent];
}

-(PSHandData*) passerHand {
    return _player.hands[_player.passer];
}

-(PSHandData*) handForPlayerId:(PSPlayerId)identifier {
    return _player.hands[identifier];
}

-(PSGameBidData*) declarerBid {
    return [self bidForPlayerId:_player.declarer];
}

-(PSGameBidData*) humanBid {
    return [self bidForPlayerId:0];
}

-(PSGameBidData*) currentPlayerBid {
    return [self bidForPlayerId:_player.current];
}

-(PSGameBidData*) halfBid {
    return [self bidForPlayerId:_player.half];
}

-(PSGameBidData*) passerBid {
    return [self bidForPlayerId:_player.passer];
}

-(PSGameBidData*)bidForPlayerId:(PSPlayerId)identifier {
    return [_player bids][identifier];
}

-(void)setBid:(PSBid*)bid forPlayer:(PSPlayerId)identifier withMax:(PSBid*)maxBid {
    [self setCurrentBid:bid forPlayer:identifier];
    [self setMaxBid:maxBid forPlayer:identifier];
}

-(void)setCurrentBid:(PSBid*)bid forPlayer:(PSPlayerId)identifier {
    _player.bids[identifier].current = bid;
}

-(void)setMaxBid:(PSBid*)bid forPlayer:(PSPlayerId)identifier {
    _player.bids[identifier].max = bid;
}

@end
